﻿using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WebCapture
{   
    public partial class MainForm2: Form
    {
        private ToolStrip toolStrip;
        private ToolStripLabel lblUrl;
        private ToolStripTextBox txtUrl;
        private ToolStripButton btnCapture;
        private ToolStripButton btnStop;
        private ToolStripButton btnChooseFolder;

        private WebView2 webView;
        private TextBox txtLog;
        private ProgressBar progressBar;
        private Label lblPercent;
        private TableLayoutPanel mainLayout;

        private const string DefaultSaveFolder = @"C:\Temp";
        private const int MAX_SINGLE_CAPTURE_DIM = 8192;

        private CancellationTokenSource captureCts;
        private string saveFolder = DefaultSaveFolder;

        private string logFilePath => Path.Combine(saveFolder, "web_capture_log.txt");

        public MainForm2()
        {
            Text = "Edge 全网页截图 - 高级工具版";
            Width = 1200;
            Height = 850;
            MinimumSize = new Size(900, 600);
            //this.WindowState = FormWindowState.Maximized;
            this.StartPosition = FormStartPosition.CenterScreen;

            // --- ToolStrip ---
            toolStrip = new ToolStrip
            {
                GripStyle = ToolStripGripStyle.Hidden,
                Dock = DockStyle.Top,
                BackColor = Color.LightGray,
                RenderMode = ToolStripRenderMode.System
            };

            lblUrl = new ToolStripLabel("请输入网址：");            
            txtUrl = new ToolStripTextBox
            {
                Text = "https://www.neau.edu.cn/",
                AutoSize = false,  // ← 禁用自动大小
                Width = 600        // ← 真实宽度
            };

            btnChooseFolder = new ToolStripButton("📁 选择保存路径");
            btnChooseFolder.Click += BtnChooseFolder_Click;

            btnCapture = new ToolStripButton("📸 开始截图");
            btnCapture.Click += BtnCapture_Click;

            btnStop = new ToolStripButton("🛑 停止");
            btnStop.Enabled = false;
            btnStop.Click += BtnStop_Click;

            toolStrip.Items.Add(lblUrl);
            toolStrip.Items.Add(txtUrl);
            toolStrip.Items.Add(btnChooseFolder);
            toolStrip.Items.Add(new ToolStripSeparator());
            toolStrip.Items.Add(btnCapture);
            toolStrip.Items.Add(btnStop);

            // --- TableLayoutPanel 主布局 ---
            mainLayout = new TableLayoutPanel
            {
                Dock = DockStyle.Fill,
                RowCount = 3,
                ColumnCount = 1
            };
            mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 65)); // WebView2
            mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 25)); // Log
            mainLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 10)); // Progress
            mainLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));

            // WebView2
            webView = new WebView2 { Dock = DockStyle.Fill };

            // Log TextBox
            txtLog = new TextBox
            {
                Dock = DockStyle.Fill,
                Multiline = true,
                ScrollBars = ScrollBars.Vertical,
                ReadOnly = true,
                Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right
            };

            // ProgressBar + Percent Label
            var progressPanel = new Panel { Dock = DockStyle.Fill };
            progressBar = new ProgressBar
            {
                Left = 6,
                Top = 6,
                Width = 1000,
                Height = 22,
                Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right
            };
            lblPercent = new Label
            {
                Left = 1010,
                Top = 6,
                Width = 80,
                Text = "0%",
                TextAlign = ContentAlignment.MiddleLeft
            };
            progressPanel.Controls.Add(progressBar);
            progressPanel.Controls.Add(lblPercent);

            mainLayout.Controls.Add(webView, 0, 0);
            mainLayout.Controls.Add(txtLog, 0, 1);
            mainLayout.Controls.Add(progressPanel, 0, 2);

            Controls.Add(mainLayout);
            Controls.Add(toolStrip);

            Load += MainForm_Load;
        }

        private async void MainForm_Load(object sender, EventArgs e)
        {
            try
            {
                AppendLog("初始化 WebView2...");
                await webView.EnsureCoreWebView2Async();
                AppendLog("WebView2 初始化完成。");
            }
            catch (Exception ex)
            {
                AppendLog("WebView2 初始化失败: " + ex.Message);
                MessageBox.Show("WebView2 初始化失败: " + ex.Message);
            }

            // 自动创建默认保存目录
            if (!Directory.Exists(DefaultSaveFolder))
            {
                Directory.CreateDirectory(DefaultSaveFolder);
                AppendLog($"已自动创建保存目录: {DefaultSaveFolder}");
            }
        }

        private void BtnChooseFolder_Click(object sender, EventArgs e)
        {
            using var fd = new FolderBrowserDialog();
            fd.SelectedPath = saveFolder;
            if (fd.ShowDialog() == DialogResult.OK)
            {
                saveFolder = fd.SelectedPath;
                AppendLog($"保存路径已修改为: {saveFolder}");
            }
        }

        private void BtnStop_Click(object sender, EventArgs e)
        {
            if (captureCts != null)
            {
                captureCts.Cancel();
                AppendLog("停止请求已发送...");
            }
        }

        private async void BtnCapture_Click(object sender, EventArgs e)
        {
            string url = txtUrl.Text.Trim();
            if (string.IsNullOrEmpty(url))
            {
                MessageBox.Show("请输入网址！");
                return;
            }

            btnCapture.Enabled = false;
            btnStop.Enabled = true;
            ResetProgressAndLogs();
            AppendLog($"开始截图: {url}");

            captureCts = new CancellationTokenSource();
            var token = captureCts.Token;

            try
            {
                var navTcs = new TaskCompletionSource<bool>();
                void navHandler(object s, CoreWebView2NavigationCompletedEventArgs ev)
                {
                    navTcs.TrySetResult(true);
                    webView.NavigationCompleted -= navHandler;
                }
                webView.NavigationCompleted += navHandler;
                webView.CoreWebView2.Navigate(url);
                AppendLog("正在加载页面...");
                await navTcs.Task;
                AppendLog("页面加载完成。");

                var finalPath = await CaptureFullPageRobustAsync(saveFolder, token);

                AppendLog($"截图保存完成: {finalPath}");
                MessageBox.Show($"截图已保存到: {finalPath}");
            }
            catch (OperationCanceledException)
            {
                AppendLog("截图已停止，保存已截取部分。");
                MessageBox.Show("截图已停止，已保存当前部分。");
            }
            catch (Exception ex)
            {
                AppendLog("截图出错: " + ex.Message);
                MessageBox.Show("截图过程中出错: " + ex.Message);
            }
            finally
            {
                btnCapture.Enabled = true;
                btnStop.Enabled = false;
                SetProgress(100, "完成");
            }
        }

        #region 日志与进度

        private void AppendLog(string text)
        {
            if (txtLog.InvokeRequired)
            {
                txtLog.Invoke(new Action(() => AppendLog(text)));
                return;
            }

            string line = $"{DateTime.Now:HH:mm:ss} - {text}";
            txtLog.AppendText(line + Environment.NewLine);

            // 写入日志文件
            try
            {
                File.AppendAllText(logFilePath, line + Environment.NewLine);
            }
            catch { }

            txtLog.SelectionStart = txtLog.Text.Length;
            txtLog.ScrollToCaret();
        }

        private void SetProgress(int percent, string statusText = null)
        {
            if (progressBar.InvokeRequired)
            {
                progressBar.Invoke(new Action(() => SetProgress(percent, statusText)));
                return;
            }
            percent = Math.Max(0, Math.Min(100, percent));
            progressBar.Value = percent;
            lblPercent.Text = percent + "%";
            if (!string.IsNullOrEmpty(statusText))
                AppendLog(statusText);
        }

        private void ResetProgressAndLogs()
        {
            progressBar.Value = 0;
            lblPercent.Text = "0%";
            txtLog.Clear();
        }

        #endregion

        #region 截图核心逻辑（支持停止和保存未完成截图）

        private async Task<string> CaptureFullPageRobustAsync(string saveFolder, CancellationToken token)
        {
            var core = webView.CoreWebView2 ?? throw new InvalidOperationException("WebView2 未初始化。");

            AppendLog("启用 Page 模块...");
            await core.CallDevToolsProtocolMethodAsync("Page.enable", "{}");

            // 注入强制展开 JS
            var helperJs = @"
(function(){
  try {document.documentElement.style.overflow='visible';document.body.style.overflow='visible';}catch(e){}
  try {document.documentElement.style.height='auto';document.body.style.height='auto';}catch(e){}
  try{var els=document.querySelectorAll('*');for(var i=0;i<els.length;i++){var s=getComputedStyle(els[i]);if(s&&(s.overflow==='hidden'||s.overflowY==='hidden'||s.overflowX==='hidden')) els[i].style.overflow='visible';els[i].style.maxHeight='none';els[i].style.minHeight='0';}}catch(e){}
  return true;
})();";
            await core.ExecuteScriptAsync(helperJs);
            AppendLog("网页已展开。");
            await Task.Delay(300);

            // 获取网页尺寸
            var raw = await core.ExecuteScriptAsync(
                "JSON.stringify({ width: Math.max(document.documentElement.scrollWidth||0, document.body.scrollWidth||0)," +
                "height: Math.max(document.documentElement.scrollHeight||0, document.body.scrollHeight||0)," +
                "viewportWidth: window.innerWidth, viewportHeight: window.innerHeight, dpr: window.devicePixelRatio||1 })");
            var json = UnwrapExecuteScriptResult(raw);
            using var doc = JsonDocument.Parse(json);
            int contentWidth = doc.RootElement.GetProperty("width").GetInt32();
            int contentHeight = doc.RootElement.GetProperty("height").GetInt32();
            int viewportWidth = doc.RootElement.GetProperty("viewportWidth").GetInt32();
            int viewportHeight = doc.RootElement.GetProperty("viewportHeight").GetInt32();
            double dpr = doc.RootElement.GetProperty("dpr").GetDouble();

            AppendLog($"网页内容尺寸: {contentWidth}x{contentHeight}, 视口: {viewportWidth}x{viewportHeight}, DPR={dpr}");

            contentWidth = Math.Max(1, contentWidth);
            contentHeight = Math.Max(1, contentHeight);

            // 判断单次截图可行性
            if (contentWidth <= MAX_SINGLE_CAPTURE_DIM && contentHeight <= MAX_SINGLE_CAPTURE_DIM)
            {
                try
                {
                    AppendLog("尝试单次截图...");
                    SetProgress(5, "单次截图");

                    var metrics = new { width = contentWidth, height = contentHeight, deviceScaleFactor = dpr, mobile = false, scale = 1 };
                    await core.CallDevToolsProtocolMethodAsync("Emulation.setDeviceMetricsOverride", JsonSerializer.Serialize(metrics));

                    var captureParams = new { format = "png", fromSurface = true };
                    var captureJson = await core.CallDevToolsProtocolMethodAsync("Page.captureScreenshot", JsonSerializer.Serialize(captureParams));
                    using var jr = JsonDocument.Parse(captureJson);
                    var base64 = jr.RootElement.GetProperty("data").GetString();
                    if (string.IsNullOrEmpty(base64)) throw new Exception("返回截图为空。");

                    var bytes = Convert.FromBase64String(base64);
                    string filename = $"网页截图_{DateTime.Now:yyyyMMdd_HHmmss}.png";
                    string path = Path.Combine(saveFolder, filename);
                    await File.WriteAllBytesAsync(path, bytes);

                    AppendLog("单次截图成功。");
                    await core.CallDevToolsProtocolMethodAsync("Emulation.clearDeviceMetricsOverride", "{}");
                    SetProgress(100, "已保存 (单次截图)");
                    return path;
                }
                catch { await core.CallDevToolsProtocolMethodAsync("Emulation.clearDeviceMetricsOverride", "{}"); AppendLog("单次截图失败，使用滚动拼接。"); }
            }
            else
            {
                AppendLog("内容过大，使用滚动拼接截图。");
            }

            return await CaptureByScrollAsync(core, contentWidth, contentHeight, viewportWidth, viewportHeight, dpr, saveFolder, token);
        }

        private async Task<string> CaptureByScrollAsync(CoreWebView2 core, int contentWidth, int contentHeight,
            int viewportWidth, int viewportHeight, double dpr, string saveFolder, CancellationToken token)
        {
            int vertSeg = (int)Math.Ceiling((double)contentHeight / viewportHeight);
            AppendLog($"滚动拼接: {vertSeg} 段");
            SetProgress(1, "开始滚动拼接");

            var images = new List<Image>();
            try
            {
                for (int i = 0; i < vertSeg; i++)
                {
                    token.ThrowIfCancellationRequested();

                    int scrollY = i * viewportHeight;
                    int segHeightCss = Math.Min(viewportHeight, contentHeight - scrollY);

                    AppendLog($"滚动到 y={scrollY} (段 {i + 1}/{vertSeg})");
                    SetProgress((int)((i / (double)vertSeg) * 70) + 1, $"滚动 {i + 1}/{vertSeg}");

                    var jsScroll = $@"
(async function(){{
  window.scrollTo(0, Math.max(0, {scrollY}-50)); 
  await new Promise(r=>setTimeout(r,120));
  window.scrollTo(0,{scrollY});
  await new Promise(r=>setTimeout(r,350));
  return true;
}})();";
                    await core.ExecuteScriptAsync(jsScroll);

                    var captureJson = await core.CallDevToolsProtocolMethodAsync("Page.captureScreenshot", "{}");
                    using var jr = JsonDocument.Parse(captureJson);
                    var base64 = jr.RootElement.GetProperty("data").GetString();
                    if (string.IsNullOrEmpty(base64)) throw new Exception($"段 {i + 1} 截图为空");

                    var bytes = Convert.FromBase64String(base64);
                    using var ms = new MemoryStream(bytes);
                    var img = Image.FromStream(ms);
                    images.Add(new Bitmap(img));

                    await Task.Delay(120);
                }

                token.ThrowIfCancellationRequested();

                AppendLog("开始拼接图片...");
                SetProgress(75, "拼接中");

                int finalWidthPx = (int)Math.Round(contentWidth * dpr);
                int finalHeightPx = (int)Math.Round(contentHeight * dpr);

                var finalBitmap = new Bitmap(finalWidthPx, finalHeightPx);
                using (var g = Graphics.FromImage(finalBitmap))
                {
                    g.Clear(Color.White);
                    int currentYPx = 0;
                    for (int i = 0; i < images.Count; i++)
                    {
                        var seg = images[i];
                        g.DrawImage(seg, 0, currentYPx, seg.Width, seg.Height);
                        currentYPx += seg.Height;
                        seg.Dispose();

                        var p = 75 + (int)(((i + 1) / (double)images.Count) * 20);
                        SetProgress(p, $"拼接 {i + 1}/{images.Count}");
                    }
                }

                string filename = captureCts.IsCancellationRequested ?
                    $"网页截图_未完成_{DateTime.Now:yyyyMMdd_HHmmss}.png" :
                    $"网页截图_拼接_{DateTime.Now:yyyyMMdd_HHmmss}.png";

                string path = Path.Combine(saveFolder, filename);
                finalBitmap.Save(path, System.Drawing.Imaging.ImageFormat.Png);
                finalBitmap.Dispose();

                AppendLog("拼接完成，恢复滚动到顶部。");
                try { await core.ExecuteScriptAsync("window.scrollTo(0,0);"); } catch { }
                SetProgress(100, $"已保存 ({(captureCts.IsCancellationRequested ? "未完成" : "完成")})");
                return path;
            }
            catch (OperationCanceledException)
            {
                if (images.Count > 0)
                {
                    AppendLog("用户停止，保存已截取部分...");
                    string path = Path.Combine(saveFolder, $"网页截图_未完成_{DateTime.Now:yyyyMMdd_HHmmss}.png");
                    int finalWidthPx = (int)Math.Round(contentWidth * dpr);
                    int finalHeightPx = (int)Math.Round(contentHeight * dpr);

                    var finalBitmap = new Bitmap(finalWidthPx, finalHeightPx);
                    using (var g = Graphics.FromImage(finalBitmap))
                    {
                        g.Clear(Color.White);
                        int currentYPx = 0;
                        foreach (var seg in images)
                        {
                            g.DrawImage(seg, 0, currentYPx, seg.Width, seg.Height);
                            currentYPx += seg.Height;
                            seg.Dispose();
                        }
                    }
                    finalBitmap.Save(path, System.Drawing.Imaging.ImageFormat.Png);
                    finalBitmap.Dispose();
                    return path;
                }
                throw;
            }
        }

        private static string UnwrapExecuteScriptResult(string raw)
        {
            if (string.IsNullOrEmpty(raw)) return "{}";
            if (raw.Length >= 2 && raw[0] == '"' && raw[^1] == '"')
            {
                var inner = raw.Substring(1, raw.Length - 2);
                inner = inner.Replace("\\\"", "\"").Replace("\\\\", "\\");
                return inner;
            }
            return raw;
        }

        #endregion
    }
}
